﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ServiceProcess;
using System.Reflection;
using System.Collections;
using System.IO;

namespace PTA.App
{
    public partial class ServiceManager : Form
    {
        private static string ServiceName = Config.GetAppSettings("ServiceName");
        private static string DisplayName = Config.GetAppSettings("DisplayName", ServiceName);
        private static string UserName = Config.GetAppSettings("Username");
        private static string Password = Config.GetAppSettings("Password");
        private static ServiceStartMode StartType = PTA.Convert.ChangeType<ServiceStartMode>(Config.GetAppSettings("StartType", "Automatic"));
        private static ServiceAccount Account = PTA.Convert.ChangeType<ServiceAccount>(Config.GetAppSettings("ServiceAccount", "LocalSystem"));
        private static ServiceManager Instance = null;
        private static FileSystemWatcher fsw = null;
        private static Dictionary<string, long> dictFilePos;

        private ServiceManager()
        {
            InitializeComponent();
            string path = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log");
            if (!System.IO.Directory.Exists(path))
            {
                System.IO.Directory.CreateDirectory(path);
            }
            dictFilePos = new Dictionary<string, long>();
            System.IO.Directory.EnumerateFiles(path, "*.txt", SearchOption.AllDirectories).ForEach(x => dictFilePos.Add(x.Replace(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log"), "").Replace(System.IO.Path.DirectorySeparatorChar, '|').Substring(1), new FileInfo(x).Length));

            fsw = new FileSystemWatcher(path, "*.TXT");
            fsw.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size;
            fsw.IncludeSubdirectories = true;
            fsw.Changed += (sender, e) =>
            {
                var filename = e.FullPath;
                var key = filename.Replace(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log"), "").Replace(System.IO.Path.DirectorySeparatorChar, '|').Substring(1);

                if (!dictFilePos.ContainsKey(key))
                {
                    dictFilePos.Add(key, 0);
                }
                using (var fs = File.OpenRead(filename))
                {
                    var filelength = fs.Length;
                    if (filelength == dictFilePos[key])
                    {
                        return;
                    }
                    fs.Position = dictFilePos[key];

                    if (filelength > fs.Position)
                    {
                        var bytes = new byte[filelength - fs.Position];
                        fs.Read(bytes, 0, bytes.Length);
                        dictFilePos[key] = filelength;
                        this.TxtInfo.ExecMethod("AppendText", new object[] { key + Environment.NewLine });
                        this.TxtInfo.ExecMethod("AppendText", new object[] { System.Text.Encoding.UTF8.GetString(bytes) + Environment.NewLine });
                    }
                    else
                    {
                        dictFilePos[key] = filelength;
                    }
                }
            };
            fsw.EnableRaisingEvents = true;
        }

        private void RefreshButton(string txt = "")
        {
            var sv = ServiceController.GetServices().Find(s => s.ServiceName.Equals(ServiceName, StringComparison.OrdinalIgnoreCase));
            //服务已安装
            if (sv != null)
            {
                this.BtnInstall.SetProperty("Enabled", false);
                this.BtnUninstall.SetProperty("Enabled", true);
                this.BtnStop.SetProperty("Enabled", sv.Status == ServiceControllerStatus.Running);
                this.BtnStart.SetProperty("Enabled", sv.Status != ServiceControllerStatus.Running);
                if (string.IsNullOrWhiteSpace(txt))
                {
                    txt = "Windows服务信息：";
                    txt += Environment.NewLine + Environment.NewLine;
                    txt += "服务名称 ：　" + ServiceName;
                    txt += Environment.NewLine;
                    txt += "显示名称 ：　" + DisplayName;
                    txt += Environment.NewLine;
                    txt += "服务状态 ：　" + sv.Status.ToString();
                }
            }
            else
            {
                this.BtnInstall.SetProperty("Enabled", true);
                this.BtnUninstall.SetProperty("Enabled", false);
                this.BtnStart.SetProperty("Enabled", false);
                this.BtnStop.SetProperty("Enabled", false);

                if (string.IsNullOrWhiteSpace(txt))
                {
                    txt = "Windows服务信息：";
                    txt += Environment.NewLine + Environment.NewLine;
                    txt += "程序路径 ：" + System.IO.Path.GetFileName(Assembly.GetEntryAssembly().Location);
                    txt += Environment.NewLine;
                    txt += "服务名称 ：" + ServiceName;
                    txt += Environment.NewLine;
                    txt += "显示名称 ：" + DisplayName;
                    txt += Environment.NewLine;
                    txt += "服务账户 ：" + Account;
                    txt += Environment.NewLine;
                    txt += "启动类型 ：" + StartType;
                    if (!string.IsNullOrWhiteSpace(UserName))
                    {
                        txt += Environment.NewLine;
                        txt += "用 户 名:" + UserName;
                        if (!string.IsNullOrWhiteSpace(Password))
                        {
                            txt += "密    码:" + Password;
                        }
                    }
                }
            }
            this.TxtInfo.ExecMethod("AppendText", new object[] { txt });
            this.TxtInfo.ExecMethod("AppendText", new object[] { Environment.NewLine });
        }

        private void BtnStart_Click(object sender, EventArgs e)
        {
            SetUIState(sender);
            this.TxtInfo.ExecMethod("AppendText", new object[] { "正在启动服务..." });
            this.TxtInfo.ExecMethod("AppendText", new object[] { Environment.NewLine });
            this.Update();
            Sys.ExecCommand(new SystemCommand() { FileName = "net", Arguments = "start " + ServiceName, Callback = this.RefreshButton });
        }

        private void BtnStop_Click(object sender, EventArgs e)
        {
            SetUIState(sender);
            this.TxtInfo.ExecMethod("AppendText", new object[] { "正在停止服务..." });
            this.TxtInfo.ExecMethod("AppendText", new object[] { Environment.NewLine });
            this.Update();
            Sys.ExecCommand(new SystemCommand() { FileName = "net", Arguments = "stop " + ServiceName, Callback = this.RefreshButton });
        }

        private void BtnInstall_Click(object sender, EventArgs e)
        {
            SetUIState(sender);
            if (string.IsNullOrWhiteSpace(ServiceName))
            {
                MessageBox.Show("服务名称不能为空");
                return;
            }
            this.TxtInfo.ExecMethod("AppendText", new object[] { "正在安装服务..." });
            this.TxtInfo.ExecMethod("AppendText", new object[] { Environment.NewLine });
            this.Update();


            var cmd = BuildServiceInstallCommand(Assembly.GetEntryAssembly().Location, ServiceName, DisplayName, Account, UserName, Password, StartType);
            cmd.Callback = delegate(string text)
            {
                if (ServiceController.GetServices().Exists(item => item.ServiceName.Equals(ServiceName, StringComparison.OrdinalIgnoreCase)))
                {
                    RefreshButton("已成功安装Windows服务【" + ServiceName + "】!");
                }
                else
                {
                    RefreshButton(text);
                    System.Windows.Forms.MessageBox.Show("安装失败!");
                }
            };
            Sys.ExecCommand(cmd);
        }

        private void BtnUninstall_Click(object sender, EventArgs e)
        {
            SetUIState(sender);
            this.TxtInfo.ExecMethod("AppendText", new object[] { "正在卸载服务..." });
            this.TxtInfo.ExecMethod("AppendText", new object[] { Environment.NewLine });
            this.Update();
            var cmd = BuildServiceUninstallCommand(Assembly.GetEntryAssembly().Location, ServiceName);
            cmd.Callback = delegate(string text)
            {
                if (ServiceController.GetServices().Exists(item => item.ServiceName.Equals(ServiceName, StringComparison.OrdinalIgnoreCase)))
                {
                    System.Windows.Forms.MessageBox.Show("卸载失败!");
                    RefreshButton(text);
                }
                else
                {
                    RefreshButton("已成功卸载Windows服务【" + ServiceName + "】!");
                }
            };
            Sys.ExecCommand(cmd);
        }

        private void BtnLogClear_Click(object sender, EventArgs e)
        {
            SetUIState(sender);
            this.TxtInfo.ExecMethod("Clear", null);
        }

        private void BtnHelp_Click(object sender, EventArgs e)
        {
            SetUIState(sender);
            string helptxt = @"请在App.config文件中AppSettings节配置服务参数:

ServiceName
服务名称(必填)

DisplayName
显示名称(如不填则同ServiceName一致)

ServiceAccount
系统账户(LocalSystem(默认)、LocalService、NetworkService、User)

ServiceStartType
启动方式(Automatic(默认),Manual,Disabled)

ServiceUsername
登录用户(可不填)

ServicePassword
登录密码(可不填)";
            this.TxtInfo.ExecMethod("AppendText", new object[] { helptxt });
            this.TxtInfo.ExecMethod("AppendText", new object[] { Environment.NewLine });
        }

        private void btnConfig_Click(object sender, EventArgs e)
        {
            SetUIState(sender);
            this.txtServiceName.Text = ServiceName;
            this.txtDisplayName.Text = DisplayName;
            this.ddlStartType.SelectedIndex = this.ddlStartType.Items.IndexOf(StartType.ToString());
            this.ddlSystemAccount.SelectedIndex = this.ddlSystemAccount.Items.IndexOf(Account.ToString());
            this.txtUserName.Text = UserName;
            this.txtPassword.Text = Password;
        }

        private void SaveConfig(object sender)
        {
            ServiceName = this.txtServiceName.Text;
            DisplayName = this.txtDisplayName.Text;
            StartType = PTA.Convert.ChangeType<ServiceStartMode>(this.ddlStartType.Text);
            Account = PTA.Convert.ChangeType<ServiceAccount>(this.ddlSystemAccount.Text);
            UserName = this.txtUserName.Text;
            Password = this.txtPassword.Text;

            var config = System.Configuration.ConfigurationManager.OpenExeConfiguration(System.Configuration.ConfigurationUserLevel.None);
            System.Xml.XmlDocument dom = new System.Xml.XmlDocument();
            dom.Load(config.FilePath);

            var appsettings = dom.SelectSingleNode("configuration/appSettings");
            if (appsettings == null)
            {
                var configuration = dom.SelectSingleNode("configuration");
                if (configuration == null)
                {
                    dom.DocumentElement.InnerXml = "<configuration><appSettings></appSettings></configuration>";
                }
                else
                {
                    configuration.InnerXml = "<appSettings></appSettings>";
                }
                appsettings = dom.SelectSingleNode("configuration/appSettings");
            }
            PrepareConfig(dom, appsettings, "ServiceName", ServiceName);
            PrepareConfig(dom, appsettings, "DisplayName", DisplayName);
            PrepareConfig(dom, appsettings, "ServiceStartType", StartType.ToString());
            PrepareConfig(dom, appsettings, "ServiceAccount", Account.ToString());
            PrepareConfig(dom, appsettings, "ServiceUsername", UserName);
            PrepareConfig(dom, appsettings, "Password", Password);
            SetUIState(sender);
            dom.Save(config.FilePath);
            RefreshButton(string.Empty);
        }

        private void btnSaveConfig_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrWhiteSpace(this.txtServiceName.Text))
            {
                MessageBox.Show("服务名称不能为空");
                return;
            }
            if (!string.IsNullOrWhiteSpace(ServiceName) && !ServiceName.Equals(this.txtServiceName.Text.Trim(), StringComparison.OrdinalIgnoreCase) && ServiceController.GetServices().Exists(item => item.ServiceName.Equals(ServiceName, StringComparison.OrdinalIgnoreCase)))
            {
                switch (MessageBox.Show("当前系统中存在名称为" + ServiceName + "的服务，是否卸载?", "卸载服务", MessageBoxButtons.YesNoCancel))
                {
                    case System.Windows.Forms.DialogResult.Cancel:
                        return;
                    case System.Windows.Forms.DialogResult.Yes:
                        var cmd = BuildServiceUninstallCommand(Assembly.GetEntryAssembly().Location, ServiceName);
                        cmd.Callback = text =>
                        {
                            if (ServiceController.GetServices().Exists(item => item.ServiceName.Equals(ServiceName, StringComparison.OrdinalIgnoreCase)))
                            {
                                System.Windows.Forms.MessageBox.Show("卸载失败!");
                                RefreshButton(text);
                            }
                            else
                            {
                                SaveConfig(sender);
                            }
                        };
                        Sys.ExecCommand(cmd);
                        break;
                    case System.Windows.Forms.DialogResult.No:
                        SaveConfig(sender);
                        break;
                }
            }
            else
            {
                SaveConfig(sender);
            }
        }

        private void PrepareConfig(System.Xml.XmlDocument dom, System.Xml.XmlNode node, string name, string value)
        {

            var current = node.SelectSingleNode(string.Format("add[key={0}]", name));
            if (current == null)
            {
                current = dom.CreateElement("add");
                node.AppendChild(current);
            }
            if (string.IsNullOrWhiteSpace(value))
            {
                node.RemoveChild(current);
                return;
            }
            if (current.Attributes["key"] == null)
            {
                var attr = dom.CreateAttribute("key");
                current.Attributes.Append(attr);
            }
            if (current.Attributes["value"] == null)
            {
                var attr = dom.CreateAttribute("value");
                current.Attributes.Append(attr);
            }
            current.Attributes["key"].Value = name;
            current.Attributes["value"].Value = value;
        }

        #region 静态方法
        /// <summary>
        /// 创建安装服务的脚本
        /// </summary>
        /// <param name="path">Windows服务应用程序路径</param>
        /// <param name="serviceName">服务名称</param>
        /// <param name="displayName">服务显示名称</param>
        /// <param name="account">服务账户</param>
        /// <param name="username">用户名</param>
        /// <param name="password">密码</param>
        /// <param name="startType">启动类型</param>
        /// <returns></returns>
        private static SystemCommand BuildServiceInstallCommand(string path, string serviceName, string displayName = null, ServiceAccount account = ServiceAccount.LocalSystem, string username = null, string password = null, ServiceStartMode startType = ServiceStartMode.Automatic)
        {
            SystemCommand x = new SystemCommand();
            x.FileName = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), @"Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe ");
            x.WorkingDirectory = System.IO.Path.GetDirectoryName(path);

            StringBuilder result = new StringBuilder();
            result.Append(@" /ServiceName=");
            result.Append(serviceName);
            result.Append(@" /DisplayName=");
            result.Append(string.IsNullOrWhiteSpace(displayName) ? serviceName : displayName);
            result.Append(@" /ServiceAccount=");
            result.Append(account.ToString());
            result.Append(@" /StartType=");
            result.Append(startType.ToString());

            if (!string.IsNullOrWhiteSpace(username))
            {
                result.Append(@" /Username=");
                result.Append(username);
                if (!string.IsNullOrWhiteSpace(password))
                {
                    result.Append(@" /Password=");
                    result.Append(password);
                }
            }

            result.Append(" \"");
            result.Append(System.IO.Path.GetFileName(path));
            result.Append("\"");
            x.Arguments = result.ToString();
            return x;
        }

        private static SystemCommand BuildServiceUninstallCommand(string path, string serviceName)
        {
            SystemCommand x = new SystemCommand();
            x.FileName = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), @"Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe ");
            x.WorkingDirectory = System.IO.Path.GetDirectoryName(path);

            StringBuilder result = new StringBuilder();
            result.Append(@"/u ");
            result.Append(@" /ServiceName=");
            result.Append(serviceName);
            result.Append(" \"");
            result.Append(System.IO.Path.GetFileName(path));
            result.Append("\"");

            x.Arguments = result.ToString();
            return x;
        }

        /// <summary>
        /// 部署Windows服务,这个方法调用一个窗口来管理当前的服务.
        /// </summary>
        /// <param name="handler">设置点击设置按钮时处理的操作,如果参数不为null,则显示设置按钮,否则不显示</param>
        /// <example>
        ///     if (Environment.UserInteractive)
        ///     {
        ///         VI.App.ServiceManager.DeployProcess();
        ///     }
        ///     else
        ///     {
        ///         服务初始化及启动代码
        ///     }
        /// </example>
        public static void DeployProcess(string title = null)
        {
            Instance = new ServiceManager();
            if (!string.IsNullOrWhiteSpace(title))
            {
                Instance.Text = title;
            }
            Instance.RefreshButton(string.Empty);
            Instance.ShowDialog();
        }
        #endregion

        private void btnCancelConfig_Click(object sender, EventArgs e)
        {
            SetUIState(sender);
        }

        private void SetUIState(object sender)
        {
            if (sender is Button)
            {
                if (((Button)sender) == btnConfig)
                {
                    this.TxtInfo.Visible = false;
                    this.paneSetting.Visible = true;

                }
                else
                {
                    this.TxtInfo.Visible = true;
                    this.paneSetting.Visible = false;
                }
            }
        }

        private void ServiceManager_FormClosing(object sender, FormClosingEventArgs e)
        {
            if(fsw != null )
            {
                fsw.EnableRaisingEvents = false;
                fsw.Dispose();
            }
            if(dictFilePos != null)
            {
                dictFilePos.Clear();
            }
        }
    }
}
